﻿using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace X22.Consul.Extension
{
    public static class X22ConsulExtension
    {
        /// <summary>
        /// 配置文件注册Consul
        /// </summary>
        /// <param name="app"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IConfiguration configuration)
        {
            var serviceName = configuration["ServiceName"];
            var consulAddress = configuration["ConsulAddress"];
            var uri = new Uri(configuration["HealthUrl"]);

            var consulOption = new ConsulOption
            {
                Service = new Service
                {
                    ServiceName = serviceName,
                    ServiceHead = uri.Scheme,
                    ServiceIP = uri.Host,
                    ServicePort = uri.Port,
                },
                ServiceHealthCheck = uri.PathAndQuery,
                ConsulAddress = consulAddress
            };
            return RegisterConsul(app, consulOption);
        }
        /// <summary>
        /// 委托Consul
        /// </summary>
        /// <param name="app"></param>
        /// <param name="option"></param>
        /// <returns></returns>
        public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, Action<ConsulOption> option)
        {
            var consulOption = new ConsulOption();
            option.Invoke(consulOption);
            return RegisterConsul(app, consulOption);
        }
        /// <summary>
        /// 注册Consul
        /// </summary>
        /// <param name="app"></param>
        /// <param name="consulOption"></param>
        /// <returns></returns>
        private static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, ConsulOption consulOption)
        {
            #region 检查配置项
            if (string.IsNullOrEmpty(consulOption.Service.ServiceName) || string.IsNullOrEmpty(consulOption.Service.ServiceIP))
            {
                throw new Exception("请检查配置项！");
            }

            if (consulOption.Service.ServiceHead != "http" && consulOption.Service.ServiceHead != "https")
            {
                throw new Exception("请检查配置项！");
            }

            if (!consulOption.ServiceHealthCheck.StartsWith("/"))
            {
                throw new Exception("健康检查url必须以 / 开头！");
            }
            #endregion

            var healthUrl = $"{consulOption.Service.ServiceHead}://{consulOption.Service.ServiceIP}:{consulOption.Service.ServicePort}{consulOption.ServiceHealthCheck}";
            Console.WriteLine($"健康检查地址：{healthUrl}");

            var consulClient = new ConsulClient(x =>
            {
                x.Address = new Uri(consulOption.ConsulAddress);
            });

            var registration = new AgentServiceRegistration()
            {
                ID = Guid.NewGuid().ToString(),
                Name = consulOption.Service.ServiceName,// 服务名
                Address = consulOption.Service.ServiceIP, // 服务绑定IP
                Port = consulOption.Service.ServicePort,//, // 服务绑定端口
                Check = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                    Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔
                    HTTP = healthUrl,//健康检查地址
                    Timeout = TimeSpan.FromSeconds(5)
                }
            };

            // 服务注册
            consulClient.Agent.ServiceRegister(registration).Wait();

            // 应用程序终止时，服务取消注册
            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).Wait();
            });

            //健康检查
            app.ServiceHealthCheck(consulOption.ServiceHealthCheck);
            return app;
        }

        /// <summary>
        /// 健康检查接口
        /// </summary>
        /// <param name="app"></param>
        /// <param name="healthUrl"></param>
        private static void ServiceHealthCheck(this IApplicationBuilder app,string healthUrl)
        {
            app.Map(healthUrl,
                app => app.Run(async context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.OK;
                    await context.Response.WriteAsync("Hello ServiceHealthCheck OK");
                })
            );


        }
        /// <summary>
        /// 添加服务发现
        /// </summary>
        /// <param name="services"></param>
        /// <param name="option"></param>
        /// <returns></returns>
        public static IServiceCollection AddConsulServiceDiscovery(this IServiceCollection services, Action<ServiceDiscoveryOption> option)
        {
            services.Configure(option);
            services.AddTransient<IConsulServiceDiscovery, ConsulServiceDiscovery>();
            return services;
        }
    }
}
